package com.sdy.healthcode.h5.config;

import com.sdy.common.utils.EncodeUtil;
import com.sdy.common.utils.RandomUtil;
import com.sdy.healthcode.biz.constants.ParamConstants;
import com.sdy.healthcode.biz.constants.RedisConstants;
import com.sdy.healthcode.biz.service.SysParamService;
import com.sdy.healthcode.biz.utils.HttpReqUtil;
import com.sdy.mvc.utils.JsonUtil;
import com.sdy.redis.service.RedisService;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Data
@Component
public class WxConfig {
    private String maAppid;
    private String maSecret;
    private String oaAppid;
    private String oaSecret;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private SysParamService sysParamService;
    
    private static WxConfig wxConfig;
    private static RedisService redisService;
    
    @PostConstruct
    public void init() {
        wxConfig = this;
        redisService = applicationContext.getBean(RedisService.class);
        this.oaAppid = sysParamService.getOrDefault(ParamConstants.Wx.WX_OA_APPID, "");
        this.oaSecret = sysParamService.getOrDefault(ParamConstants.Wx.WX_OA_APPSECRET, "");
    }

    public interface WxApi {
        String CODE2SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=%s&grant_type=authorization_code";
        String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}";
        String JS_API_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${ACCESS_TOKEN}&type=jsapi";
        String OA_CODE_TO_SESSION = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APPID}&secret=${SECRET}&code=${CODE}&grant_type=authorization_code";
        String OA_TEMPLATE_MSG_SEND = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${ACCESS_TOKEN}";
    }
    
    public interface ReqMethod {
        String GET = "GET";
        String POST = "POST";
    }
    
    public static Map<String, Object> requestMa(String api, String method, Object... params) {
        api = api.replace("${APPID}", wxConfig.getMaAppid()).replace("${SECRET}", wxConfig.getMaSecret());
        String requestUrl = String.format(api, params);
        String resp = "{}";
        if (ReqMethod.GET.equals(method)) {
            resp = HttpReqUtil.get(requestUrl, StandardCharsets.UTF_8.name(), 2000, null);
        } else if (ReqMethod.POST.equals(method)) {
            resp = HttpReqUtil.postJson(requestUrl, "", StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8.name(), 2000, null);
        }
        Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
        checkWxRespStatus(m);
        return m;
    }

    public static Map<String, Object> oaCodeToSession(String code) {
        String requestUrl = WxApi.OA_CODE_TO_SESSION
                .replace("${APPID}", wxConfig.getOaAppid())
                .replace("${SECRET}", wxConfig.getOaSecret())
                .replace("${CODE}", code);
        String resp = HttpReqUtil.get(requestUrl, StandardCharsets.UTF_8.name(), 2000, null);
        Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
        checkWxRespStatus(m);
        return m;
    }
    
    private static void checkWxRespStatus(Map<String, Object> m) {
        if (!m.containsKey("errcode") || (Integer) m.get("errcode") == 0) {
            return;
        }
        throw new RuntimeException(m.get("errmsg") + "(" + m.get("errcode") + ")");
    }
    
    public static synchronized String getOaAccessToken() {
        String accessToken = redisService.get(RedisConstants.WX_ACCESS_TOKEN + wxConfig.getOaAppid(), String.class);
        if (accessToken == null) {
            String api = WxApi.ACCESS_TOKEN.replace("${APPID}", wxConfig.getOaAppid()).replace("${SECRET}", wxConfig.getOaSecret());
            String resp = HttpReqUtil.get(api, StandardCharsets.UTF_8.name(), 2000, null);
            Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
            checkWxRespStatus(m);
            accessToken = (String) m.get("access_token");
            int expireIn = (Integer) m.get("expires_in");
            redisService.set(RedisConstants.WX_ACCESS_TOKEN + wxConfig.getOaAppid(), accessToken, expireIn - 200);
            log.info("获取微信access_token：{}", accessToken);
        }
        return accessToken;
    }
    
    public static synchronized String getOaJsApiTicket() {
        String jsApiTicket = redisService.get(RedisConstants.WX_JS_API_TICKET + wxConfig.getOaAppid(), String.class);
        if (jsApiTicket == null) {
            String accessToken = getOaAccessToken();
            String api = WxApi.JS_API_TICKET.replace("${ACCESS_TOKEN}", accessToken);
            String resp = HttpReqUtil.get(api, StandardCharsets.UTF_8.name(), 2000, null);
            Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
            checkWxRespStatus(m);
            jsApiTicket = (String) m.get("ticket");
            int expireIn = (Integer) m.get("expires_in");
            redisService.set(RedisConstants.WX_JS_API_TICKET + wxConfig.getOaAppid(), jsApiTicket, expireIn - 200);
            log.info("获取微信js_api_ticket：{}", jsApiTicket);
        }
        return jsApiTicket;
    }
    
    public static synchronized Integer sendTemplateMsg(TemplateMsg templateMsg) {
        String accessToken = getOaAccessToken();
        String api = WxApi.OA_TEMPLATE_MSG_SEND.replace("${ACCESS_TOKEN}", accessToken);
        Map<String, Object> msg = new HashMap<>();
        msg.put("touser", templateMsg.getOpenId());
        msg.put("template_id", templateMsg.getTemplateId());
        msg.put("url", templateMsg.getUrl());
        msg.put("data", templateMsg.getData());
        String resp = HttpReqUtil.postJson(api, JsonUtil.toJson(msg), StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8.name(), 2000, null);
        Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
        checkWxRespStatus(m);
        return (Integer) m.get("msgid");
    }

    /**
     * JS-SDK使用权限签名
     */
    public static Map<String, String> createJsSdkSigniture(String url) {
        String noncestr = RandomUtil.produceStringAndNumber(16);
        String jsapiTicket = getOaJsApiTicket();
        String timestamp = System.currentTimeMillis() / 1000 + "";
        List<Pair<String, String>> paramList = new ArrayList<>();
        paramList.add(Pair.of("noncestr", noncestr));
        paramList.add(Pair.of("jsapi_ticket", jsapiTicket));
        paramList.add(Pair.of("timestamp", timestamp));
        paramList.add(Pair.of("url", url));
        paramList.sort(Comparator.comparing(Pair::getKey));
        String params = paramList.stream().map(item -> item.getKey() + "=" + item.getValue()).collect(Collectors.joining("&"));
        String signature = EncodeUtil.sha1(params);
        Map<String, String> result = new HashMap<>();
        result.put("timestamp", timestamp);
        result.put("noncestr", noncestr);
        result.put("signature", signature);
        result.put("appid", wxConfig.getOaAppid());
        return result;
    }

    @Data
    @Accessors(chain = true)
    public static class TemplateMsg {
        private String templateId;
        private String openId;
        private String url;
        private String miniprogram;
        private String pagepath;
        private Map<String, TemplateMsgValue> data;
        private String color;

        @Data
        @Accessors(chain = true)
        public static class TemplateMsgValue {
            private String value;
            private String color;
        }
    }
}
